﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Reflection;

namespace rehot.core
{
    using SanlieHash;

    public class HotAttributes
    {
        internal Dictionary<string, HotAttribute> _attrs { get; set; }
        internal Dictionary<string, StringBuilder> _attrs_map { get; set; }
        internal Dictionary<string, HotType> _attr_types { get; set; }
        internal void Lose()
        {
            if (_attrs != null)
            {
                foreach (var attr in _attrs.Values) attr.Dispose();
                _attrs.Clear();
                _attrs = null;
            }
            if (_attr_types != null)
            {
                foreach (var attr in _attr_types.Values) attr.Dispose();
                _attr_types.Clear();
                _attr_types = null;
            }
            if (_attrs_map != null)
            {
                _attrs_map.Clear();
                _attrs_map = null;
            }
        }
        internal HotAttributes(HotType hotType)
        {
            _attrs = new Dictionary<string, HotAttribute>();
            _attr_types = new Dictionary<string, HotType>();
            _attrs_map = new Dictionary<string, StringBuilder>();
            record_attr(hotType._type.GetCustomAttributes(true), hotType.Id);
            foreach (var method in hotType.Methods._methods)
            record_attr(method.Value.GetCustomAttributes(true), method.Key);
            foreach (var method in hotType.Members._propertys)
            record_attr(method.Value.GetCustomAttributes(true), method.Key);
            foreach (var method in hotType.Members._fields)
            record_attr(method.Value.GetCustomAttributes(true), method.Key);
            foreach (var method in hotType.Events._events)
            record_attr(method.Value.GetCustomAttributes(true), method.Key);
        }
        internal void record_attr(object[] runtime_attrs, string parentId)
        {
            if (runtime_attrs == null || runtime_attrs.Length == 0) return;

            foreach (var attr in runtime_attrs)
            {
                var runtime_type = attr.GetType();
                var type_key = FastHash.GetSM3(HotAttribute.GetStdName(runtime_type.Name));
                var key = FastHash.GetSM3(parentId + type_key);
                if (!_attr_types.ContainsKey(type_key)) _attr_types.Add(type_key, new HotType(runtime_type, false));
                if (!_attrs.ContainsKey(key)) _attrs.Add(key, new HotAttribute(_attr_types[type_key], attr));
                if (!_attrs_map.ContainsKey(parentId)) _attrs_map[parentId] = new StringBuilder(key);
                else _attrs_map[parentId].Append("\n").Append(key);
            }
        }
        internal HotAttributes(HotDLL dll)
        {
            _Load_Assembly(dll.assembly, dll.Id);
        }
        internal HotAttributes(HotOneDLL dll)
        {
            _Load_Assembly(dll.assembly, dll.Id);
        }
        internal void _Load_Assembly(Assembly assembly, string id)
        {
            var runtime_attrs = assembly.GetCustomAttributes(true);
            _attrs = new Dictionary<string, HotAttribute>(runtime_attrs.Length);
            _attr_types = new Dictionary<string, HotType>(runtime_attrs.Length);
            _attrs_map = new Dictionary<string, StringBuilder>(runtime_attrs.Length);
            record_attr(runtime_attrs, id);
        }
        internal void store(string id, object attr)
        {
            if (!_attrs.ContainsKey(id)) _attrs.Add(id, new HotAttribute(attr));
        }
        public IEnumerable<HotAttribute> ReadAttributesByParentId(string parentId)
        {
            if (_attrs_map.ContainsKey(parentId)) foreach (var attr_id in _attrs_map[parentId].ToString().Split('\n')) yield return _attrs[attr_id];
        }
        public IEnumerable<string> FindAttributesByParentId(string parentId)
        {
            if (_attrs_map.ContainsKey(parentId)) foreach (var attr_id in _attrs_map[parentId].ToString().Split('\n')) yield return attr_id;
        }
        public HotAttribute ReadAttributeById(string id)
        {
            if (_attrs.ContainsKey(id)) return _attrs[id];
            throw new KeyNotFoundException(id);
        }
        public string ReadNameById(string id)
        {
            if (_attrs.ContainsKey(id)) return _attrs[id].Name;
            return string.Empty;
        }
        public bool Exist(string parentId, string name)
        {
            var id = FindAttributeId(parentId, name);
            return id != string.Empty;
        }
        public HotAttribute ReadAttribute(string parentId, string name)
        {
            var id = FindAttributeId(parentId, name);
            if (id != string.Empty) return _attrs[id];
            throw new KeyNotFoundException(id);
        }
        public string FindAttributeId(string parentId, string name)
        {
            if (_attrs_map.ContainsKey(parentId))
            {
                var type_key = FastHash.GetSM3(HotAttribute.GetStdName(name));
                if (_attr_types.ContainsKey(type_key))
                {
                    var key = FastHash.GetSM3(parentId + type_key);
                    if (_attrs.ContainsKey(key)) return key;
                }
            }
            return string.Empty;
        }

    }
}
